import _ from 'lodash';
import moduleAdd from './sorted/add';
import moduleRemove from './sorted/remove';
import moduleUnion from './sorted/union';
import moduleIntersect from './sorted/intersect';

/* eslint-disable */
module.exports = function (redisClient, module) {
    const helpers = module.helpers.redis;
    moduleAdd(redisClient, module);
    moduleRemove(redisClient, module);
    moduleUnion(redisClient, module);
    moduleIntersect(redisClient, module);

    function sortedSetRange(method, key, start, stop, withScores, callback) {
        if (Array.isArray(key)) {
            return module.sortedSetUnion({
                method,
                sets: key,
                start,
                stop,
                withScores
            }, callback);
        }

        const params = [key, start, stop];
        if (withScores) {
            params.push('WITHSCORES');
        }

        redisClient[method](params, (err, data) => {
            if (err) {
                return callback(err);
            }
            if (!withScores) {
                return callback(null, data);
            }
            const objects = [];
            for (let i = 0; i < data.length; i += 2) {
                objects.push({ value: data[i], score: parseFloat(data[i + 1]) });
            }
            callback(null, objects);
        });
    }

    module.getSortedSetRange = function (key, start, stop, callback) {
        sortedSetRange('zrange', key, start, stop, false, callback);
    };

    module.getSortedSetRevRange = function (key, start, stop, callback) {
        sortedSetRange('zrevrange', key, start, stop, false, callback);
    };

    module.getSortedSetRangeWithScores = function (key, start, stop, callback) {
        sortedSetRange('zrange', key, start, stop, true, callback);
    };

    module.getSortedSetRevRangeWithScores = function (key, start, stop, callback) {
        sortedSetRange('zrevrange', key, start, stop, true, callback);
    };

    module.getSortedSetRangeByScore = function (key, start, count, min, max, callback) {
        redisClient.zrangebyscore([key, min, max, 'LIMIT', start, count], callback);
    };

    module.getSortedSetRevRangeByScore = function (key, start, count, max, min, callback) {
        redisClient.zrevrangebyscore([key, max, min, 'LIMIT', start, count], callback);
    };

    function sortedSetRangeByScoreWithScores(method, key, start, count, min, max, callback) {
        redisClient[method]([key, min, max, 'WITHSCORES', 'LIMIT', start, count], (err, data) => {
            if (err) {
                return callback(err);
            }
            const objects = [];
            for (let i = 0; i < data.length; i += 2) {
                objects.push({ value: data[i], score: parseFloat(data[i + 1]) });
            }
            callback(null, objects);
        });
    }

    module.getSortedSetRangeByScoreWithScores = function (key, start, count, min, max, callback) {
        sortedSetRangeByScoreWithScores('zrangebyscore', key, start, count, min, max, callback);
    };

    module.getSortedSetRevRangeByScoreWithScores = function (key, start, count, max, min, callback) {
        sortedSetRangeByScoreWithScores('zrevrangebyscore', key, start, count, max, min, callback);
    };


    module.sortedSetCount = function (key, min, max, callback) {
        redisClient.zcount(key, min, max, callback);
    };

    module.sortedSetCard = function (key, callback) {
        redisClient.zcard(key, callback);
    };

    module.sortedSetsCard = function (keys, callback) {
        if (Array.isArray(keys) && !keys.length) {
            return callback(null, []);
        }
        const multi = redisClient.multi();
        for (let i = 0; i < keys.length; i += 1) {
            multi.zcard(keys[i]);
        }
        multi.exec(callback);
    };

    module.sortedSetRank = function (key, value, callback) {
        redisClient.zrank(key, value, callback);
    };

    module.sortedSetsRanks = function (keys, values, callback) {
        const multi = redisClient.multi();
        for (let i = 0; i < values.length; i += 1) {
            multi.zrank(keys[i], values[i]);
        }
        multi.exec(callback);
    };

    module.sortedSetRanks = function (key, values, callback) {
        const multi = redisClient.multi();
        for (let i = 0; i < values.length; i += 1) {
            multi.zrank(key, values[i]);
        }
        multi.exec(callback);
    };

    module.sortedSetRevRank = function (key, value, callback) {
        redisClient.zrevrank(key, value, callback);
    };

    module.sortedSetScore = function (key, value, callback) {
        if (!key || value === undefined) {
            return callback(null, null);
        }

        redisClient.zscore(key, value, (err, score) => {
            if (err) {
                return callback(err);
            }
            if (score === null) {
                return callback(null, score);
            }
            callback(null, parseFloat(score));
        });
    };

    module.sortedSetsScore = function (keys, value, callback) {
        helpers.multiKeysValue(redisClient, 'zscore', keys, value, callback);
    };

    module.sortedSetScores = function (key, values, callback) {
        helpers.multiKeyValues(redisClient, 'zscore', key, values, callback);
    };

    module.isSortedSetMember = function (key, value, callback) {
        module.sortedSetScore(key, value, (err, score) => {
            callback(err, _.isNumber(score));
        });
    };

    module.isSortedSetMembers = function (key, values, callback) {
        helpers.multiKeyValues(redisClient, 'zscore', key, values, (err, results) => {
            if (err) {
                return callback(err);
            }
            callback(null, results.map(Boolean));
        });
    };

    module.isMemberOfSortedSets = function (keys, value, callback) {
        helpers.multiKeysValue(redisClient, 'zscore', keys, value, (err, results) => {
            if (err) {
                return callback(err);
            }
            callback(null, results.map(Boolean));
        });
    };

    module.getSortedSetsMembers = function (keys, callback) {
        const multi = redisClient.multi();
        for (let i = 0; i < keys.length; i += 1) {
            multi.zrange(keys[i], 0, -1);
        }
        multi.exec(callback);
    };

    module.sortedSetIncrBy = function (key, increment, value, callback) {
        redisClient.zincrby(key, increment, value, (err, newValue) => {
            callback(err, !err ? parseFloat(newValue) : undefined);
        });
    };

    function sortedSetLex(method, reverse, key, min, max, start, count, callback) {
        callback = callback || start;

        let minmin;
        let maxmax;
        if (reverse) {
            minmin = '+';
            maxmax = '-';
        } else {
            minmin = '-';
            maxmax = '+';
        }

        if (min !== minmin && !min.match(/^[[(]/)) {
            min = `[${min}`;
        }
        if (max !== maxmax && !max.match(/^[[(]/)) {
            max = `[${max}`;
        }

        if (count) {
            redisClient[method]([key, min, max, 'LIMIT', start, count], callback);
        } else {
            redisClient[method]([key, min, max], callback);
        }
    }

    module.getSortedSetRangeByLex = function (key, min, max, start, count, callback) {
        sortedSetLex('zrangebylex', false, key, min, max, start, count, callback);
    };

    module.getSortedSetRevRangeByLex = function (key, max, min, start, count, callback) {
        sortedSetLex('zrevrangebylex', true, key, max, min, start, count, callback);
    };

    module.sortedSetRemoveRangeByLex = function (key, min, max, callback) {
        callback = callback || helpers.noop;
        sortedSetLex('zremrangebylex', false, key, min, max, callback);
    };

    module.sortedSetLexCount = function (key, min, max, callback) {
        sortedSetLex('zlexcount', false, key, min, max, callback);
    };
};
/* eslint-enable */
